Expand description
Drop-in replacements for buffered I/O types in std::io
.
These replacements retain the method names/signatures and implemented traits of their stdlib counterparts, making replacement as simple as swapping the import of the type:
§BufReader
:
- use std::io::BufReader;
+ use buf_redux::BufReader;
§BufWriter
:
- use std::io::BufWriter;
+ use buf_redux::BufWriter;
§LineWriter
:
- use std::io::LineWriter;
+ use buf_redux::LineWriter;
§More Direct Control
All replacement types provide methods to:
- Increase the capacity of the buffer
- Get the number of available bytes as well as the total capacity of the buffer
- Consume the wrapper without losing data
BufReader
provides methods to:
- Access the buffer through an
&
-reference without performing I/O - Force unconditional reads into the buffer
- Get a
Read
adapter which empties the buffer and then pulls from the inner reader directly - Shuffle bytes down to the beginning of the buffer to make room for more reading
- Get inner reader and trimmed buffer with the remaining data
BufWriter
and LineWriter
provides methods to:
- Flush the buffer and unwrap the inner writer unconditionally.
- Get the inner writer and trimmed buffer with the unflushed data.
§More Sensible and Customizable Buffering Behavior
Tune the behavior of the buffer to your specific use-case using the types in the
policy
module:
-
Refine
BufReader
’s behavior by implementing theReaderPolicy
trait or use an existing implementation likeMinBuffered
to ensure the buffer always contains a minimum number of bytes (until the underlying reader is empty). -
Refine
BufWriter
’s behavior by implementing the [WriterPolicy
trait] or use an existing implementation likeFlushOn
to flush when a particular byte appears in the buffer (used to implementLineWriter
).
§Making Room
The buffered types of this crate and their std::io
counterparts, by default, use Box<[u8]>
as their buffer types (Buffer
is included as well since it is used internally
by the other types in this crate).
When one of these types inserts bytes into its buffer, via BufRead::fill_buf()
(implicitly
called by Read::read()
) in BufReader
’s case or Write::write()
in BufWriter
’s case,
the entire buffer is provided to be read/written into and the number of bytes written is saved.
The read/written data then resides in the [0 .. bytes_inserted]
slice of the buffer.
When bytes are consumed from the buffer, via BufRead::consume()
or Write::flush()
,
the number of bytes consumed is added to the start of the slice such that the remaining
data resides in the [bytes_consumed .. bytes_inserted]
slice of the buffer.
The std::io
buffered types, and their counterparts in this crate with their default policies,
don’t have to deal with partially filled buffers as BufReader
only reads when empty and
BufWriter
only flushes when full.
However, because the replacements in this crate are capable of reading on-demand and flushing less than a full buffer, they can run out of room in their buffers to read/write data into even though there is technically free space, because this free space is at the head of the buffer where reading into it would cause the data in the buffer to become non-contiguous.
This isn’t technically a problem as the buffer could operate like VecDeque
in std
and return
both slices at once, but this would not fit all use-cases: the Read::fill_buf()
interface only
allows one slice to be returned at a time so the older data would need to be completely consumed
before the newer data can be returned; BufWriter
could support it as the Write
interface
doesn’t make an opinion on how the buffer works, but because the data would be non-contiguous
it would require two flushes to get it all, which could degrade performance.
The obvious solution, then, is to move the existing data down to the beginning of the buffer
when there is no more room at the end so that more reads/writes into the buffer can be issued.
This works, and may suit some use-cases where the amount of data left is small and thus copying
it would be inexpensive, but it is non-optimal. However, this option is provided
as the .make_room()
methods, and is utilized by policy::MinBuffered
and policy::FlushExact
.
§Ringbuffers / slice-deque
Feature
Instead of moving data, however, it is also possible to use virtual-memory tricks to allocate a ringbuffer that loops around on itself in memory and thus is always contiguous, as described in the Wikipedia article on Ringbuffers.
This is the exact trick used by the slice-deque
crate,
which is now provided as an optional feature slice-deque
exposed via the
new_ringbuf()
and with_capacity_ringbuf()
constructors added to the buffered types here.
When a buffered type is constructed using one of these functions, .make_room()
is turned into
a no-op as consuming bytes from the head of the buffer simultaneously makes room at the tail.
However, this has some caveats:
-
It is only available on target platforms with virtual memory support, namely fully fledged OSes such as Windows and Unix-derivative platforms like Linux, OS X, BSD variants, etc.
-
The default capacity varies based on platform, and custom capacities are rounded up to a multiple of their minimum size, typically the page size of the platform. Windows’ minimum size is comparably quite large (64 KiB) due to some legacy reasons, so this may be less optimal than the default capacity for a normal buffer (8 KiB) for some use-cases.
-
Due to the nature of the virtual-memory trick, the virtual address space the buffer allocates will be double its capacity. This means that your program will appear to use more memory than it would if it was using a normal buffer of the same capacity. The physical memory usage will be the same in both cases, but if address space is at a premium in your application (32-bit targets) then this may be a concern.
Modules§
- Types which can be used to tune the behavior of
BufReader
andBufWriter
.
Macros§
- Shorthand for
return DoRead(bool)
orreturn DoRead(true)
(empty invocation) - Shorthand for
return FlushAmt(n)
orreturn FlushAmt(0)
(empty invocation)
Structs§
- A drop-in replacement for
std::io::BufReader
with more functionality. - A drop-in replacement for
std::io::BufWriter
with more functionality. - A deque-like datastructure for managing bytes.
- The error type for
BufWriter::into_inner()
, contains theBufWriter
as well as the error that occurred. - A drop-in replacement for
std::io::LineWriter
with more functionality. - A
Read
adapter for a consumedBufReader
which will empty bytes from the buffer before reading fromR
directly. Frees the buffer when it has been emptied.
Functions§
- Copy data between a
BufRead
and aWrite
without an intermediate buffer. - Set a thread-local handler for errors thrown in
BufWriter
’sDrop
impl.